#ifndef Algebra_TimeSteppers_h
#define Algebra_TimeSteppers_h

#include "AlgebraModule.h"
#include "ODESolver.h"
#include "LinearSolver.h"

#include <memory>

SG_NAMESPACE_OPEN

namespace Algebra
{
    // // Forward declarations
    // class LinearSolver;

    /**
     * Base class for second-order ordinary time integrators.
     */
    class ALGEBRA_EXPORT SecondOrderTimeStepper : public SecondOrderODESolver
    {
      public:
        SecondOrderTimeStepper ();
        virtual ~SecondOrderTimeStepper ();

        /// initialize the ODE solver.
        void Initialize (SecondOrderODESystem *odes) override;

      protected:
        bool                          mFactorizationIsOk = false;
        std::shared_ptr<LinearSolver> mSolver            = nullptr;
    };

    /**
     * Central Difference Method
     */
    class ALGEBRA_EXPORT CentralDifference : public SecondOrderTimeStepper
    {
      public:
        CentralDifference ();
        virtual ~CentralDifference ();

        /// Perform a single time step from time t to time t+dt.
        void Step (Vec_t &u, Vec_t &v, Vec_t &a) override;

      private:
        Vec_t                         U_km1;  //< The U field at the k-1 timelevel
    };

    /**
     * Modified Central Difference Method
     */
    class ALGEBRA_EXPORT ModifiedCentralDifference : public SecondOrderTimeStepper
    {
      public:
        ModifiedCentralDifference ();
        virtual ~ModifiedCentralDifference ();

        /// Perform a single time step from time t to time t+dt.
        void Step (Vec_t &u, Vec_t &v, Vec_t &a) override;

      private:
        Vec_t                         U_km1;  //< The U at the k-1 timelevel
        Vec_t                         F_km1;  //< The F at the k-1 timelevel
        Vec_t                         F_k;    //< The F at the k timelevel
    };

    /**
     * Newmark-Beta Method
     *
     * Newmark N M. A Method of Computation for Structural Dynamics.
     * Proc Asce, 1959, 85(1):67-94.
     * DOI:doi:http://dx.doi.org/.
     */
    class ALGEBRA_EXPORT NewmarkBeta : public SecondOrderTimeStepper
    {
    public:
      NewmarkBeta ();
      virtual ~NewmarkBeta ();

      /// Perform a single time step from time t to time t+dt.
      void Step (Vec_t &u, Vec_t &v, Vec_t &a) override;

    private:
      Real_t                        beta, gamma;
    };

    /**
     * Wilson-Theta Method
     *
     * Wilson E L. A computer program for the dynamic stress analysis of
     * underground structures. dynamic loads, 1968.
     */
    class ALGEBRA_EXPORT WilsonTheta : public SecondOrderTimeStepper
    {
      public:
        WilsonTheta ();
        virtual ~WilsonTheta ();

        /// Perform a single time step from time t to time t+dt.
        void Step (Vec_t &u, Vec_t &v, Vec_t &a) override;

      private:
        Real_t                        theta              = 1.4;
        Vec_t                         F_k;  //< The previous RHS
    };

    /**
     * Generalized-Alpha Method
     *
     * Chung J. A Time Integration Algorithm for Structural Dynamics With Improved
     * Numerical Dissipation: The Generalized-α Method.
     * Journal of Applied Mechanics, 1993, 60(2):371.
     * DOI:10.1115/1.2900803.
     * rho_inf in [0,1]
     */
    class ALGEBRA_EXPORT GeneralizedAlpha : public SecondOrderTimeStepper
    {
      public:
        GeneralizedAlpha ();
        virtual ~GeneralizedAlpha ();

        /// Perform a single time step from time t to time t+dt.
        void Step (Vec_t &u, Vec_t &v, Vec_t &a) override;

      private:
        Real_t                        beta, gamma, alpha_m, alpha_f;
        Vec_t                         F_k;  //< The previous RHS
    };

    /**
     * Bathe Method
     *
     * Batheg K J. On a composite implicit time integration procedure for
     * nolinear dynamics. Computer & Structures. 2005,83(31/32):2513-2524
     */
    class ALGEBRA_EXPORT Bathe : public SecondOrderTimeStepper
    {
      public:
        Bathe ();
        virtual ~Bathe ();

        /// initialize the ODE solver.
        void Initialize (SecondOrderODESystem *odes) override;

        /// Perform a single time step from time t to time t+dt.
        void Step (Vec_t &u, Vec_t &v, Vec_t &a) override;

      private:
        Real_t                        gamma = 0.5;
        Vec_t                         F_k;  //< The previous RHS
        std::shared_ptr<LinearSolver> mSolver2 = nullptr;
    };

}  // namespace Algebra

SG_NAMESPACE_CLOSE

#endif  // Algebra_TimeSteppers_h